﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using BeerScanatronUI.ScanatronService;

namespace BeerScanatronUI
{
    public partial class Kiosk
    {
        // State variables for State Machine.
        private Drinker _drinker;
        private Beer _beer;
        private string FatalErrorUIInstructions = null;

        /// <summary>
        /// Perform actions dictated by ScanCode in current MachineState.
        /// Stimulus may result in MachineState change.
        /// </summary>
        /// <param name="scanCode">Reset entire state machine and UI if input is null.</param>
        public void ProcessScanCode(ScanCode scanCode)
        {
            if (scanCode == null)
            {
                _state = StateMachine.Initialize;
            }

            // Initialize working vars.
            IServer server = GetService<IServer>();
            StateMachine initialState;

            do
            {
                // Initial state.
                initialState = _state;

                // Response to ScanCode based on current state.
                switch (_state)
                {
                    case StateMachine.Initialize:
                        // User input not accepted at this point.  Ignore it.
                        _drinker = null;
                        _beer = null;
                        scanCode = null;
                        lblName.Text = "";
                        lblInstructions.Text = "";
                        txtBarcode.Text = "";
                        ((IUserImage)userImageMain).GetUserImageFromAppResources("female.png");

                        _state = StateMachine.ScanUserCode;
                        txtBarcode.Focus();
                        break;

                    case StateMachine.ScanUserCode:
                        // Initialize UI.
                        _drinker = null;
                        _beer = null;
                        lblName.Text = "";
                        lblInstructions.Text = FatalErrorUIInstructions != null 
                            ? FatalErrorUIInstructions : "Scan guest bar code";
                        txtBarcode.Focus();

                        // Test scan code if present.
                        if (scanCode != null && scanCode.IsNumericCode)
                        {
                            // See if valid user ID.
                            Drinker drinker = server.GetDrinker(scanCode.Code);
                            if (drinker == null)
                            {
                                // Invalid user.
                                PlaySound.Play(PlaySound.Sounds.UnknownCode);
                                lblName.Text = String.Format("Unknown guest ID: " + scanCode.Code);
                                break;
                            }

                            // Display drinker picture then continue to validate.
                            ((IUserImage)userImageMain).SetUserImage(server.GetUserImage(drinker.BarCode));

                            // Make sure drinker can be served.
                            if (!drinker.CanServe)
                            {
                                // Invalid user.
                                PlaySound.Play(PlaySound.Sounds.DrinkerCannotBeServed);
                                lblName.Text = String.Format("{0} - Do not serve", drinker.FullName());
                                ((IUserImage)userImageMain).SetImageEffects(UserImage.Effects.DoNotAllow);
                                break;
                            }

                            // Test for daily limit.                           
                            if (server.GetDrinkerBeersConsumedToday(drinker.BarCode) >= _beerDailyLimit)
                            {
                                // Too many drinks!
                                PlaySound.Play(PlaySound.Sounds.BeverageLimitReached);
                                lblName.Text = String.Format("{0} - Beverage limit reached", drinker.FullName());
                                ((IUserImage)userImageMain).SetImageEffects(UserImage.Effects.DoNotAllow);
                                break;
                            }

                            // User can drink. Save user info and go to next state.
                            _drinker = drinker;
                            ((IUserImage)userImageMain).SetImageEffects(UserImage.Effects.Allow);
                            _state = StateMachine.ScanBeverageCode;
                        }
                        break;

                    case StateMachine.ScanBeverageCode:
                        // Initialize UI.
                        lblName.Text = String.Format("{0} - {1}",
                            _drinker.FullName(),
                            server.GetDrinkerBeersConsumedToday(_drinker.BarCode));
                        lblInstructions.Text = "Scan beverage bar code";
                        txtBarcode.Focus();

                        // Allow server to throw out last user scanned.
                        if (scanCode != null && scanCode.IsCancel)
                        {
                            _state = StateMachine.Initialize;
                            ((IUserImage)userImageMain).SetImageEffects(UserImage.Effects.None);
                            _drinker = null;
                            break;
                        }

                        // Test scan code if present.
                        if (scanCode != null && scanCode.IsNumericCode)
                        {
                            // See if valid beverage ID.
                            Beer beer = server.GetBeer(scanCode.Code);
                            if (beer == null)
                            {
                                // Invalid user.
                                PlaySound.Play(PlaySound.Sounds.UnknownCode);
                                lblName.Text = String.Format("Unknown beverage ID: " + scanCode.Code);
                                break;
                            }

                            lblName.Text = String.Format("{0} - {1}",  _drinker.FullName(), beer.Name);
                            _beer = beer;
                            _state = StateMachine.RecordTransaction;
                        }
                        break;

                    case StateMachine.RecordTransaction:
                        lblInstructions.Text = "Saving transaction.";
                        PlaySound.Play(PlaySound.Sounds.TransactionCompleted);
                        txtBarcode.Focus();
                        FlowImages(((IUserImage)userImageMain).GetUserImage(), _beer.Name);

                        server.DispenseBeer(_drinker.BarCode, _beer.BarCode);

                        _state = StateMachine.Initialize;
                        break;
                    default:
                        break;
                }

                // If new state needs to be entered, clear scan code so only changes will be UI.
                if (initialState != _state)
                {
                    scanCode = null;
                }
            }
            while (initialState != _state);
        }

        /// <summary>
        /// Move images and input down right side.
        /// Note that images are initially not visible.  They are enabled as data flows down to them.
        /// </summary>
        /// <param name="image"></param>
        /// <param name="text"></param>
        private void FlowImages(Image image, string text)
        {
            // Move info down the side.
            lblImageSide2.Text = lblImageSide1.Text;
            ((IUserImage)userImageSide2).SetUserImage(((IUserImage)userImageSide1).GetUserImage());
            ((IUserImage)userImageSide2).SetImageEffects(((IUserImage)userImageSide1).GetImageEffects());
            userImageSide2.Visible = lblImageSide2.Text.Length > 0;

            lblImageSide1.Text = text;
            ((IUserImage)userImageSide1).SetUserImage(image);
            ((IUserImage)userImageSide1).SetImageEffects(((IUserImage)userImageMain).GetImageEffects());
            userImageSide1.Visible = lblImageSide1.Text.Length > 0;
        }
    }
}
